#!/usr/bin/env python

###############################################################################
# Name: KTaskTimer.py                                                         #
# Author: Marijo Mihelcic <marijo.mihelcic@gmail.com>                         #
# Copyright: (c) 2009 Marijo Mihelcic <marijo.mihelcic@gmail.com>             #
# Licence: GNU General Public License v3                                      #
###############################################################################

import sys
from PyQt4 import QtCore, QtGui
import datetime
from lib import KTaskEditDialog, KTaskReport, KTaskTimerDB
from gui import KTaskTimerUI

APP_VERSION = '0.3.1'

""" Timer specific constants """
TIMER_PERIOD        = 1000      # 1000 miliseconds - 1 second
TIMER_HOUR_PERIOD   = 60 * 60   # 3600 seconds = 1 hour
TIMER_MINUTE_PERIOD = 60        # 60 seconds = 1 minute

class TimeTrackApp(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)
        self.ui = KTaskTimerUI.Ui_TaskTimer()
        self.ui.setupUi(self)

        """ data source """
        self.dataSource         = KTaskTimerDB.DataSource()
        self.projects_list_data = self.dataSource.getProjectsLists()

        self._populateProjectsList()
        self._setEventListeners()
        self._setTimerButtonState(self.ui.btnStart, 'disabled')
        self._setTimerButtonState(self.ui.btnStop, 'disabled')
        self._taskMenuToggle('disable')

        """ Timer """
        self._timerRunning = False;
        self._total_time   = 0
        self.timer = QtCore.QTimer(self)
        self.connect(self.timer, QtCore.SIGNAL("timeout()"), self.updateTimeStatus)

    def _setEventListeners(self):

        """ Context Menu for projects lists """
        self.ui.projectsList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.connect(self.ui.projectsList, QtCore.SIGNAL('customContextMenuRequested(QPoint)'), self.on_projectsLists_contextMenuEvent)

        """ Context Menu for tasks lists """
        self.ui.tasksList.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.connect(self.ui.tasksList, QtCore.SIGNAL('customContextMenuRequested(QPoint)'), self.on_tasksLists_contextMenuEvent)

        """ Task Context Menu """
        self.connect(self.ui.actionNew_Task, QtCore.SIGNAL('triggered()'), self.on_actionNew_Task_triggered)
        self.connect(self.ui.actionEdit_Task, QtCore.SIGNAL('triggered()'), self.on_actionEdit_Task_triggered)
        self.connect(self.ui.actionDelete_Task, QtCore.SIGNAL('triggered()'), self.on_actionDelete_Task_triggered)
        self.connect(self.ui.actionGenerate, QtCore.SIGNAL('triggered()'), self.on_actionGenerate_Report_triggered)

        """ Timer Start Button """
        self.connect(self.ui.btnStart, QtCore.SIGNAL("clicked()"), self._startTimer)

        """ Timer Stop Button """
        self.connect(self.ui.btnStop, QtCore.SIGNAL("clicked()"), self._stopTimer)

        """ Close program """
        self.connect(self.ui.actionClose, QtCore.SIGNAL("triggered()"), self, QtCore.SLOT("close()"))

        """ About program """
        self.connect(self.ui.actionAbout, QtCore.SIGNAL("triggered()"), self.on_about_menu_triggered)


    def on_about_menu_triggered(self):
        QtGui.QMessageBox.about(self, self.tr("About Simple Task Timer"),
            self.tr("<b>Simple Task Timer</b> version " + APP_VERSION + "<br/>"
                    "(C)2009 Marijo Mihelcic<br/><br/>"
                    "License: GPLv3<br/>"
                    "Simple Task Timer<br/><br/>"
                    "Developed by Marijo Mihelcic"))


    def _setTimerButtonState(self, button, state):
        if (state == 'disabled'):
            button.setEnabled(False)
        else :
            button.setEnabled(True)

    def _populateProjectsList(self):
        self.ui.projectsList.clear()
        for project_id in self.projects_list_data:
            item = QtGui.QTreeWidgetItem([self.projects_list_data[project_id]])
            item.project_id = project_id
            self.ui.projectsList.addTopLevelItem(item)
        self.ui.projectsList.sortItems(0, QtCore.Qt.AscendingOrder)
        self.ui.tasksList.sortItems(0, QtCore.Qt.AscendingOrder)

    def _populateTasksList(self, project_id, task_search_string=''):
        self.ui.tasksList.clear()
        self.tasks_list_data = self.dataSource.getProjectTasksLists(project_id, task_search_string)
        for task_id in self.tasks_list_data:
            item=QtGui.QTreeWidgetItem([self.tasks_list_data[task_id]])
            item.task_id = task_id
            self.ui.tasksList.addTopLevelItem(item)
        self.ui.tasksList.sortItems(0, QtCore.Qt.AscendingOrder)

    def on_projectSearch_textChanged(self):
        project_search_text     = unicode(self.ui.projectSearch.text())
        self.projects_list_data = self.dataSource.getProjectsLike(project_search_text)
        self._populateProjectsList()

    def on_projectsLists_contextMenuEvent(self, pos):
        current_item = self.ui.projectsList.itemAt(pos)
        if current_item is None:
            """ Disabling projects editing - nothing selected """
            self.ui.actionEdit_Project.setEnabled(False)
            self.ui.actionDelete_Project.setEnabled(False)
        else:
            self.ui.actionEdit_Project.setEnabled(True)
            self.ui.actionDelete_Project.setEnabled(True)

        self.ui.projectsList.setFocus()
        self.ui.projectsList.setCurrentItem(current_item)
        menu = self.ui.menuProjects
        menu.exec_(self.ui.projectsList.mapToGlobal(pos))

    def on_projectsList_itemClicked(self):
        current_item = self.ui.projectsList.currentItem()
        if current_item is not None:
            self._populateTasksList(current_item.project_id)
            self._taskMenuToggle('disable')
            if self._timerRunning is False:
                self._setTimerButtonState(self.ui.btnStart, 'disabled')

    def on_actionNew_Project_triggered(self, checked=None):
        if checked is None:
            return
        self.showProjectDialog('new')

    def on_actionEdit_Project_triggered(self, checked=None):
        if checked is None:
            return

        item = self.ui.projectsList.currentItem()
        if not item: # None selected, so we don't know what to delete!
            return

        self.showProjectDialog('edit', item.project_id)

    def on_actionDelete_Project_triggered(self, checked=None):
        if checked is None:
            return

        item = self.ui.projectsList.currentItem()
        if not item: # None selected, so we don't know what to delete!
            return

        project_name    = self.projects_list_data[item.project_id]
        dialog_message  = 'This will delete project:'
        dialog_message  += "\n" + project_name + "\n"
        dialog_message  += 'and all project tasks and project tasks progress'

        reply = QtGui.QMessageBox.warning(self, self.tr("Project delete"),
                                          dialog_message,
                                          self.tr("Delete"),
                                          self.tr("Cancel"))

        if reply == 0:
            self.dataSource.deleteData({"type": 'project'})
            self.projects_list_data = self.dataSource.getProjectsLists();
            self._populateProjectsList()
            self.ui.tasksList.clear()

    def showProjectDialog(self, action='new', project_id=None):
        dialog_title = 'Add new project'
        project_name = ''
        if action == 'edit':
            dialog_title = 'Edit project'
            project_name = self.projects_list_data[project_id]

        input_project_name, ok = QtGui.QInputDialog.getText(self, self.tr(dialog_title),
                                              self.tr("Project name:"), QtGui.QLineEdit.Normal,
                                              project_name
                                              )
        if ok and not input_project_name.isEmpty():
            self.dataSource.storeProjectData({"action": action, "project_id" : project_id, "project_name" : unicode(input_project_name)})
            self.projects_list_data = self.dataSource.getProjectsLists();
            self._populateProjectsList()
        if ok and input_project_name.isEmpty():
            QtGui.QMessageBox.information(self, self.tr(dialog_title), 'Project name empty')
            self.showProjectDialog(action, project_id)

    def _taskMenuToggle(self, action='enable'):
        if action == 'enable':
            self.ui.actionEdit_Task.setEnabled(True)
            self.ui.actionDelete_Task.setEnabled(True)
        else :
            self.ui.actionEdit_Task.setEnabled(False)
            self.ui.actionDelete_Task.setEnabled(False)

    def on_taskSearch_textChanged(self):
        task_search_text     = unicode(self.ui.taskSearch.text())
        selected_project     = self.ui.projectsList.currentItem()
        if selected_project is not None:
            self._populateTasksList(selected_project.project_id, task_search_text)
            if self._timerRunning is False:
                self._setTimerButtonState(self.ui.btnStart, 'disabled')

    def on_tasksLists_contextMenuEvent(self, pos):
        current_item = self.ui.tasksList.itemAt(pos)
        if current_item is None:
            """ Disabling task editing - nothing selected """
            self._taskMenuToggle('disable')
        else:
            self._taskMenuToggle('enable')

        self.ui.tasksList.setFocus()
        self.ui.tasksList.setCurrentItem(current_item)
        menu = self.ui.menuTasks
        menu.exec_(self.ui.tasksList.mapToGlobal(pos))

    def on_tasksList_itemClicked(self):
        current_item=self.ui.tasksList.currentItem()
        if current_item is not None:
            task_description    = self.dataSource.getTaskDescription(current_item.task_id);
            self.ui.taskDescription.setPlainText(task_description)
            self._taskMenuToggle('enable')
            if self._timerRunning is False:
                self._setTimerButtonState(self.ui.btnStart, 'enabled')


    def on_actionNew_Task_triggered(self, checked=None):
        if checked is None:
            return
        self.showTaskDialog('new')

    def on_actionEdit_Task_triggered(self, checked=None):
        if checked is None:
            return
        item = self.ui.tasksList.currentItem()
        if not item: # None selected, so we don't know what to delete!
            return

        self.showTaskDialog('edit', item.task_id)

    def on_actionDelete_Task_triggered(self, checked=None):

        if checked is None:
            return

        item = self.ui.tasksList.currentItem()
        if not item: # None selected, so we don't know what to delete!
            return

        task_name       = self.tasks_list_data[item.task_id]
        dialog_message  = 'This will delete task:'
        dialog_message  += "\n" + task_name + "\n"
        dialog_message  += 'and all task progress'

        reply = QtGui.QMessageBox.warning(self, self.tr("Task delete"),
                                          dialog_message,
                                          self.tr("Delete"),
                                          self.tr("Cancel"))

        if reply == 0:
            self.dataSource.deleteData({"type": 'task'})
            selected_project     = self.ui.projectsList.currentItem()
            self._populateTasksList(selected_project.project_id)

    def showTaskDialog(self, action='new', task_id=None):

        task_dialog     = KTaskEditDialog.TaskEditDialog(self)
        project_item    = self.ui.projectsList.currentItem()

        cnt             = 0
        selected_pos    = None

        """ Projects sorting """
        projects_sorted = self.projects_list_data.items()
        projects_sorted.sort(key=lambda x: x[1])

        """ Populating data """
        for project_data in projects_sorted:
            task_dialog.ui.projectsSelect.addItem(project_data[1],QtCore.QVariant(project_data[0]))
            if project_item is not None and project_data[0] == project_item.project_id:
                selected_pos = cnt
            cnt+=1

        dialog_title = 'Add new task'
        if action == 'edit':
            dialog_title = 'Edit task'
            task_data = self.dataSource.getTaskData(task_id)
            task_dialog.ui.TaskName.setText(task_data['name'])
            task_dialog.ui.TasDescription.setPlainText(task_data['description'])

        if selected_pos is not None:
            task_dialog.ui.projectsSelect.setCurrentIndex(selected_pos)

        task_dialog.ui.TaskName.setFocus()
        task_dialog.setWindowTitle(dialog_title)

        """ Dialog submit handling """
        if task_dialog.exec_() == QtGui.QDialog.Accepted:
            current_item        = task_dialog.ui.projectsSelect.currentIndex()
            selected_project_id = task_dialog.ui.projectsSelect.itemData(current_item).toInt()[0]
            task_name           = QtCore.QString(task_dialog.ui.TaskName.text())
            task_description    = task_dialog.ui.TasDescription.toPlainText()
            if not task_name.isEmpty():
                self.dataSource.storeTaskData({"action": action, "project_id": selected_project_id, "task_id": task_id, "task_name" : unicode(task_name), "task_description": unicode(task_description)})
                if project_item is None:
                    self._findProjectInList(selected_project_id)
                    self._populateTasksList(selected_project_id)
                else:
                    self._populateTasksList(project_item.project_id)
            else:
                QtGui.QMessageBox.information(self, self.tr(dialog_title), 'Task name empty')
                self.showTaskDialog(action, task_id)

    def _findProjectInList(self, project_id):
        total_rows = self.ui.projectsList.topLevelItemCount()
        for project_pos in range (total_rows):
            item_index  = self.ui.projectsList.model().index(project_pos, 0)
            item        = self.ui.projectsList.itemFromIndex(item_index)
            if item is not None:
                if item.project_id == project_id:
                    self.ui.projectsList.setCurrentItem(item)

    def _startTimer(self):
        if self._timerRunning is False:
            self._total_time    = 0
            self._timerRunning  = True
            self._showTimerStatus()

            selected_project            = self.ui.projectsList.currentItem()
            selected_task               = self.ui.tasksList.currentItem()
            self._timer_project_id      = selected_project.project_id
            self._timer_taks_id         = selected_task.task_id
            self._timer_start_datetime  = datetime.datetime.now()

            selected_project_task_data  = self.dataSource.getSelectedProjectTaskData(self._timer_project_id, self._timer_taks_id)
            self.ui.projectName.setText(selected_project_task_data['project_name'])
            self.ui.taskName.setText(selected_project_task_data['task_name'])

            self._setTimerButtonState(self.ui.btnStart, 'disabled')
            self._setTimerButtonState(self.ui.btnStop, 'enabled')
            self.timer.start(1000)

    def _stopTimer(self):
        if self._timerRunning is True:
            self._timerRunning = False
            self._setTimerButtonState(self.ui.btnStop, 'disabled')
            self.on_tasksList_itemClicked()
            self.timer.stop()
            self.saveTaskProgress()

    def updateTimeStatus(self):
        self._total_time+=1
        self._showTimerStatus()

    def _showTimerStatus(self):
        hours   = int(self._total_time / TIMER_HOUR_PERIOD)
        minutes = int((self._total_time - (hours * TIMER_HOUR_PERIOD)) / TIMER_MINUTE_PERIOD)
        seconds = int(self._total_time - (hours * TIMER_HOUR_PERIOD) - (minutes * TIMER_MINUTE_PERIOD))
        self.ui.timeCounter.setText('%02d:%02d:%02d' % (hours, minutes , seconds))

    def saveTaskProgress(self):
        stop_datime     = datetime.datetime.now()
        task_progress   = self.ui.taskProgress.toPlainText()

        minutes = int(self._total_time / TIMER_MINUTE_PERIOD)
        seconds = int(self._total_time - (minutes * TIMER_MINUTE_PERIOD))

        if seconds > 30:
            minutes = minutes + 1
        save_data = {"project_id"   : self._timer_project_id,
                     "task_id"      : self._timer_taks_id,
                     "start_time"   : self._timer_start_datetime.strftime("%Y-%m-%d %H:%M:%S"),
                     "stop_time"    : stop_datime.strftime("%Y-%m-%d %H:%M:%S"),
                     "progress"     : unicode(task_progress),
                     "total_time"   : minutes
                     }
        self.dataSource.storeProgressData(save_data)
        QtGui.QMessageBox.information(self, self.tr("Task progress"), 'Task progress saved')

    def on_actionGenerate_Report_triggered(self):
        report_dialog = KTaskReport.TaskReportDialog(self)
        report_dialog.show()



if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    timetrack = TimeTrackApp()
    timetrack.show()
    sys.exit(app.exec_())
